#include "analyserview.h"
#include "globaltype.h"
#include "libs/base/service/downloader.h"
#include "libs/controls/toastlabel.h"
#include "libs/core/model/attentionmodel.h"
#include "libs/core/model/livemodel.h"
#include "libs/core/model/model.h"
#include "libs/core/model/presentermodel.h"
#include "libs/core/model/recordmodel.h"
#include "main/modules/recorder/liverecorder.h"
#include "services/yingceservice.h"
#include "ui_analyserview.h"
#include "usersettings.h"
#include "utils/utils.h"
#include "widgets/checkboxitemdelegate.h"
#include "widgets/nofocusitemdelegate.h"
#include "yingce.h"
#include <QCursor>
#include <QDesktopServices>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QListView>
#include <QMenu>
#include <QScrollBar>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTableView>
#include <QUrl>

AnalyserView::AnalyserView(yc::Extension* extension, QWidget* parent)
    : yc::ExtensionView { extension, parent }, ui(new Ui::AnalyserView)
{
    presenter_model_ = new QStandardItemModel(this);
    filter_model_ = new QSortFilterProxyModel(this);
    filter_model_->setSourceModel(presenter_model_);
    filter_model_->setFilterCaseSensitivity(Qt::CaseInsensitive);
    filter_model_->setFilterKeyColumn(0);
    filter_model_->setFilterRole(Qt::DisplayRole);

    record_model_ = new QStandardItemModel(this);
    record_model_->setHorizontalHeaderLabels(
            QStringList { tr(""), tr("Number"), tr("StartTime"), tr("Duration"), tr("Filename") });

    ui->setupUi(this);
    ui->all_checkbox->setEnabled(false);
    ui->del_button->setEnabled(false);
    ui->open_button->setEnabled(false);
    ui->locate_button->setEnabled(false);
    ui->presenter_view->setModel(filter_model_);
    ui->presenter_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->presenter_view->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->presenter_view->setItemDelegate(new NoFocusItemDelegate(this));
    ui->presenter_view->setIconSize(QSize(36, 36));

    ui->record_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->record_view->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->record_view->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->record_view->setItemDelegate(new NoFocusItemDelegate(this));
    ui->record_view->setItemDelegateForColumn(CHECK_STATE_COLUMN, new CheckBoxItemDelegate(this));
    ui->record_view->setShowGrid(false);
    ui->record_view->setContextMenuPolicy(Qt::CustomContextMenu);
    ui->record_view->setModel(record_model_);

    auto header = ui->record_view->verticalHeader();
    header->hide();
    header->setDefaultSectionSize(32);
    header = ui->record_view->horizontalHeader();
    header->setSectionResizeMode(QHeaderView::ResizeMode::Fixed);
    header->setSectionResizeMode(FILENAME_COLUMN, QHeaderView::Stretch);
    header->setSectionsClickable(false);
    header->setSectionsMovable(false);
    header->setDefaultSectionSize(160);
    header->resizeSection(CHECK_STATE_COLUMN, 40);
    header->resizeSection(NUMBER_COLUMN, 60);

    initPresenterList();

    connect(ui->search_edit, &QLineEdit::editingFinished, this, [this] {
        filter_model_->setFilterWildcard(QString("*%1*").arg(ui->search_edit->text().trimmed()));
    });

    auto am = YingCe::instance()->model()->attentionModel();
    connect(am, &yc::AttentionModel::modelReset, this, [this] {
        presenter_model_->setRowCount(0);
        record_model_->setRowCount(0);
    });
    connect(am, &yc::AttentionModel::added, this, &AnalyserView::addAttention);
    connect(am, &yc::AttentionModel::removed, this, [this](yc::Attention* attention) {
        int count = presenter_model_->rowCount();
        for (int i = 0; i < count; ++i) {
            if (presenter_model_->item(i, 0)->data(DataRole::ID_ROLE).toInt() == attention->id) {
                presenter_model_->removeRow(i);
                record_model_->setRowCount(0);
                ui->all_checkbox->setCheckState(Qt::Unchecked);
                ui->del_button->setEnabled(false);
                ui->open_button->setEnabled(false);
                ui->locate_button->setEnabled(false);
                break;
            }
        }
        updateAllCheckState();
        updateUiStatus();
    });

    auto rm = YingCe::instance()->model()->recordModel();
    connect(rm, &yc::RecordModel::modelReset, this, [this] { record_model_->setRowCount(0); });
    connect(rm, &yc::RecordModel::added, this, &AnalyserView::addRecord);
    connect(rm, &yc::RecordModel::removed, this, [this](yc::RecordInfo* record) {
        int count = record_model_->rowCount();
        for (int i = 0; i < count; ++i) {
            if (record_model_->item(i, 0)->data(DataRole::ID_ROLE).toInt() == record->id) {
                record_model_->removeRow(i);
                break;
            }
        }
        updateAllCheckState();
        updateUiStatus();
    });

    connect(ui->presenter_view->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
            &AnalyserView::onCurrentPresenterChanged);
    connect(ui->record_view, &QTableView::doubleClicked, this, &AnalyserView::openRecord);
    connect(ui->record_view, &QTableView::customContextMenuRequested, this,
            &AnalyserView::showRecordContextMenu);
    connect(ui->record_view->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
            [this](const QModelIndex& current, const QModelIndex& previous) {
                Q_UNUSED(previous)
                ui->open_button->setEnabled(current.isValid());
                ui->locate_button->setEnabled(current.isValid());
            });
    connect(record_model_, &QStandardItemModel::itemChanged, this, [this](QStandardItem* item) {
        if (item->column() == CHECK_STATE_COLUMN) {
            updateAllCheckState();
            updateUiStatus();
        }
    });
    connect(ui->all_checkbox, &QCheckBox::clicked, this, [this](bool checked) {
        int count = record_model_->rowCount();
        for (int i = 0; i < count; ++i) {
            auto item = record_model_->item(i, CHECK_STATE_COLUMN);
            if (item->isEnabled()) {
                item->setData(checked ? Qt::Checked : Qt::Unchecked, DataRole::CHECK_STATE_ROLE);
            }
        }
        updateUiStatus();
    });
    connect(ui->del_button, &QPushButton::clicked, this, &AnalyserView::deleteRecords);
    connect(ui->open_button, &QPushButton::clicked, this, &AnalyserView::openCurrentRecord);
    connect(ui->locate_button, &QPushButton::clicked, this, &AnalyserView::locateCurrentRecord);
    connect(ui->record_view->verticalScrollBar(), &QScrollBar::valueChanged, this,
            [this](int value) {
                // 当接近底部时
                if (value >= ui->record_view->verticalScrollBar()->maximum() - 10) {
                    loadMoreRecords();
                }
            });

    connect(YingCe::instance()->downloader(), &yc::Downloader::downloaded, this,
            &AnalyserView::updateAvatar);
}

void AnalyserView::initPresenterList()
{
    auto item = new QStandardItem(tr("Screen Capture"));
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setData(0, DataRole::ID_ROLE);
    item->setIcon(QIcon { ":/images/screencapture-list.png" });
    presenter_model_->appendRow(item);

    auto as = YingCe::instance()->model()->attentionModel()->attentions();
    for (const auto p : as) {
        addAttention(p);
    }
}

void AnalyserView::addAttention(yc::Attention* attention)
{
    Q_ASSERT(attention);
    auto item = new QStandardItem;
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setData(attention->id, DataRole::ID_ROLE);
    presenter_model_->appendRow(item);

    if (auto presenter =
                YingCe::instance()->model()->presenterModel()->presenter(attention->presenter)) {
        item->setText(presenter->nickname);
        const auto entry = YingCe::instance()->model()->liveModel()->webEntry(presenter->platform);
        item->setText(presenter->nickname + "\n(" + entry.name + ")");
        // Avatar
        if (!presenter->avatar.isEmpty()) {
            const auto filename = QUrl { presenter->avatar }.fileName();
            const auto path = UserSettings::instance().resDir().absoluteFilePath(filename);
            if (QFileInfo::exists(path)) {
                item->setIcon(QIcon { path });
            }
        }
    }
}

void AnalyserView::addRecord(yc::RecordInfo* record)
{
    int row = record_model_->rowCount();
    // CheckState
    auto item = new QStandardItem;
    item->setData(record->id, DataRole::ID_ROLE);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
    item->setTextAlignment(Qt::AlignCenter);
    record_model_->appendRow(item);
    // Number
    item = new QStandardItem(QString::number(row + 1));
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setTextAlignment(Qt::AlignCenter);
    record_model_->setItem(row, NUMBER_COLUMN, item);
    // CreateTime
    item = new QStandardItem(record->start_time.toString("yyyy/MM/dd hh:mm:ss"));
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setTextAlignment(Qt::AlignCenter);
    record_model_->setItem(row, START_TIME_COLUMN, item);
    // Duration
    item = new QStandardItem(utils::clockDisplayString(record->duration));
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setTextAlignment(Qt::AlignCenter);
    record_model_->setItem(row, DURATION_COLUMN, item);
    // Filename
    QString missing;
    if (record->presenter == 0) {
        if (!QFileInfo::exists(QDir { UserSettings::instance().screenCaptureSettings().save_dir }
                                       .absoluteFilePath(record->url))) {
            missing = tr("(Missing)");
        }
    } else {
        if (const auto a = YingCe::instance()->model()->attentionModel()->find(record->presenter)) {
            const QDir dir { LiveRecorder::createAttentionDirectory(a->id) };
            if (!QFileInfo::exists(dir.absoluteFilePath(record->url))) {
                missing = tr("(Missing)");
            }
        }
    }
    item = new QStandardItem(record->url + missing);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    item->setTextAlignment(Qt::AlignCenter);
    if (!missing.isEmpty()) {
        item->setForeground(QBrush(QColor(0xEA4335)));
    }
    record_model_->setItem(row, FILENAME_COLUMN, item);

    // Ui status
    ui->all_checkbox->setEnabled(true);
}

void AnalyserView::onCurrentPresenterChanged(const QModelIndex& current,
                                             const QModelIndex& previous)
{
    Q_UNUSED(previous)
    record_model_->setRowCount(0);
    int presenter = 0;
    int id = current.data(DataRole::ID_ROLE).toInt();
    if (id != 0) {
        if (auto a = YingCe::instance()->model()->attentionModel()->attention(id)) {
            presenter = a->presenter;
        } else {
            return;
        }
    }
    auto rm = YingCe::instance()->model()->recordModel();
    if (rm->hasPresenterTotalCount(presenter)) {
        for (const auto i : rm->records()) {
            if (i->presenter == presenter) {
                addRecord(i);
            }
        }
    } else {
        YingCeService::instance()->getRecords(
                presenter, 0,
                yc::ResponseHandler(
                        this, [this](const yc::Request& request, const yc::Response& response) {
                            Q_UNUSED(request)
                            if (response.status() == yc::OK_CODE) {
                                const QJsonObject jo = response.data().toObject();
                                const int presenter = jo["presenter"].toInt(0);
                                const int count = jo["count"].toInt(0);
                                if (count > 0) {
                                    const QJsonArray ja = jo["records"].toArray();
                                    for (const auto i : ja) {
                                        YingCe::instance()->model()->recordModel()->add(
                                                yc::RecordInfo { i.toObject() });
                                    }
                                }
                                YingCe::instance()->model()->recordModel()->setPresenterTotalCount(
                                        presenter, count);
                            } else {
                                yc::ToastLabel::showMessage(tr("Failed to get records."), this);
                            }
                        }));
    }

    // Ui status
    updateAllCheckState();
    updateUiStatus();
}

void AnalyserView::showRecordContextMenu(const QPoint& pos)
{
    if (pos.x() < 40)
        return;

    const QModelIndex current = ui->record_view->indexAt(pos);
    if (!current.isValid()) {
        return;
    }

    ui->record_view->selectRow(current.row());

    QMenu menu(this);
    QAction* open_action = new QAction(tr("Open"), &menu);
    menu.addAction(open_action);
    QAction* locate_action = new QAction(tr("Open location"), &menu);
    menu.addAction(locate_action);
    QAction* del_action = new QAction(tr("Delete"), &menu);
    menu.addAction(del_action);
    connect(open_action, &QAction::triggered, this, &AnalyserView::openCurrentRecord);
    connect(locate_action, &QAction::triggered, this, &AnalyserView::locateCurrentRecord);
    connect(del_action, &QAction::triggered, this, &AnalyserView::deleteCurrentRecord);
    menu.exec(QCursor::pos());
}

void AnalyserView::updateAllCheckState()
{
    int checked_count = 0;
    int count = record_model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = record_model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(DataRole::CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            checked_count++;
        }
    }
    if (checked_count > 0 && checked_count == count) {
        ui->all_checkbox->setCheckState(Qt::Checked);
    } else if (checked_count == 0) {
        ui->all_checkbox->setCheckState(Qt::Unchecked);
    } else {
        ui->all_checkbox->setCheckState(Qt::PartiallyChecked);
    }
}

void AnalyserView::updateUiStatus()
{
    int count = record_model_->rowCount();
    ui->all_checkbox->setEnabled(count > 0);
    ui->del_button->setEnabled(count > 0 && canDelete());
}

bool AnalyserView::canDelete() const
{
    int count = record_model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = record_model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(DataRole::CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            return true;
        }
    }
    return false;
}

void AnalyserView::openCurrentRecord()
{
    openRecord(ui->record_view->currentIndex());
}

void AnalyserView::locateCurrentRecord()
{
    const auto current = ui->record_view->currentIndex();
    if (current.isValid()) {
        auto item = record_model_->item(current.row(), CHECK_STATE_COLUMN);
        int id = item->data(DataRole::ID_ROLE).toInt();
        const auto ri = YingCe::instance()->model()->recordModel()->record(id);
        Q_ASSERT(ri);
        if (ri->presenter == 0) {
            const QString filename = record_model_->item(current.row(), FILENAME_COLUMN)->text();
            if (!filename.isEmpty()) {
                utils::showFileInFolder(
                        QDir { UserSettings::instance().screenCaptureSettings().save_dir }
                                .absoluteFilePath(filename));
            }
        } else {
            if (const auto a = YingCe::instance()->model()->attentionModel()->find(ri->presenter)) {
                const QDir dir { LiveRecorder::createAttentionDirectory(a->id) };
                const QString filename =
                        record_model_->item(current.row(), FILENAME_COLUMN)->text();
                utils::showFileInFolder(dir.absoluteFilePath(filename));
            }
        }
    }
}

void AnalyserView::deleteCurrentRecord()
{
    const auto current = ui->record_view->currentIndex();
    if (current.isValid()) {
        auto item = record_model_->item(current.row(), CHECK_STATE_COLUMN);
        int id = item->data(DataRole::ID_ROLE).toInt();
        YingCeService::instance()->deleteRecords(
                QList<int> { id },
                yc::ResponseHandler(this,
                                    [id](const yc::Request& request, const yc::Response& response) {
                                        if (response.status() == yc::OK_CODE) {
                                            YingCe::instance()->model()->recordModel()->remove(id);
                                        }
                                    }));
    }
}

void AnalyserView::deleteRecords()
{
    QList<int> ids;
    int count = record_model_->rowCount();
    for (int i = 0; i < count; ++i) {
        auto item = record_model_->item(i, CHECK_STATE_COLUMN);
        if (item->data(DataRole::CHECK_STATE_ROLE).toInt() == Qt::Checked) {
            ids.push_back(item->data(DataRole::ID_ROLE).toInt());
        }
    }
    if (!ids.empty()) {
        // Ui status
        ui->del_button->setEnabled(false);

        YingCeService::instance()->deleteRecords(
                ids,
                yc::ResponseHandler(
                        this, [ids](const yc::Request& request, const yc::Response& response) {
                            if (response.status() == yc::OK_CODE) {
                                for (const auto id : ids) {
                                    YingCe::instance()->model()->recordModel()->remove(id);
                                }
                            }
                        }));
    }
}

void AnalyserView::loadMoreRecords()
{
    int presenter = 0;
    int id = ui->presenter_view->currentIndex().data(DataRole::ID_ROLE).toInt();
    if (id != 0) {
        if (auto a = YingCe::instance()->model()->attentionModel()->attention(id)) {
            presenter = a->presenter;
        } else {
            return;
        }
    }

    auto rm = YingCe::instance()->model()->recordModel();
    int start_index = rm->presenterCurrentCount(presenter);
    if (!rm->hasPresenterTotalCount(presenter)
        || start_index < rm->presenterTotalCount(presenter)) {
        YingCeService::instance()->getRecords(
                presenter, start_index,
                yc::ResponseHandler(
                        this, [this](const yc::Request& request, const yc::Response& response) {
                            Q_UNUSED(request)
                            if (response.status() == yc::OK_CODE) {
                                const QJsonObject jo = response.data().toObject();
                                const int presenter = jo["presenter"].toInt(0);
                                const int count = jo["count"].toInt(0);
                                if (count > 0) {
                                    const QJsonArray ja = jo["records"].toArray();
                                    for (const auto i : ja) {
                                        YingCe::instance()->model()->recordModel()->add(
                                                yc::RecordInfo { i.toObject() });
                                    }
                                }
                                YingCe::instance()->model()->recordModel()->setPresenterTotalCount(
                                        presenter, count);
                            } else {
                                yc::ToastLabel::showMessage(tr("Failed to get records."), this);
                            }
                        }));
    }
}

void AnalyserView::openRecord(const QModelIndex& index)
{
    if (index.isValid()) {
        auto item = record_model_->item(index.row(), CHECK_STATE_COLUMN);
        int id = item->data(DataRole::ID_ROLE).toInt();
        const auto ri = YingCe::instance()->model()->recordModel()->record(id);
        Q_ASSERT(ri);
        if (ri->presenter == 0) {
            const QString filename = record_model_->item(index.row(), FILENAME_COLUMN)->text();
            if (!filename.isEmpty()) {
                QDesktopServices::openUrl(QUrl::fromLocalFile(
                        QDir { UserSettings::instance().screenCaptureSettings().save_dir }
                                .absoluteFilePath(filename)));
            }
        } else {
            if (const auto a = YingCe::instance()->model()->attentionModel()->find(ri->presenter)) {
                const QDir dir { LiveRecorder::createAttentionDirectory(a->id) };
                const QString filename = record_model_->item(index.row(), FILENAME_COLUMN)->text();
                if (!filename.isEmpty()) {
                    QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absoluteFilePath(filename)));
                }
            }
        }
    }
}

void AnalyserView::updateAvatar(const QString& filename)
{
    const auto name = QFileInfo { filename }.fileName();
    auto am = YingCe::instance()->model()->attentionModel();
    auto pm = YingCe::instance()->model()->presenterModel();
    int count = presenter_model_->rowCount();
    for (int i = 0; i < count; ++i) {
        if (auto p = am->attention(presenter_model_->item(i, 0)->data(ID_ROLE).toInt())) {
            if (auto q = pm->presenter(p->presenter)) {
                if (!q->avatar.isEmpty() && QUrl(q->avatar).fileName() == name) {
                    presenter_model_->item(i, 0)->setIcon(QIcon(filename));
                    break;
                }
            }
        }
    }
}
